import logging, log

# data account logger
logger_account = logging.getLogger('Master.SAccount')


class StockAccount(object):
    def __init__(self, money=0.0, stock=0.0, stock_type='fund'):
        """
        Init the account info
        :param money: total money init
        :param stock: total stock init
        """
        self.start_money = money  # start money
        self.stock_type = stock_type  # 'fund' or 'stock' for different tax

        self.realtime_money = money  # money in the account
        self.stock = stock  # stock money have bought in, number * average
        self.stock_invest = 0  # pay on the stock

        self.number = 0  # stock numbers *100
        self.price = 0  # stock price

        self.average = 0  # average stock price
        self.tax = 0  # tax when buy or sell

        self.profit = 0  # real time money - start_money = profit
        self.profit_percent = 0  # profit percent
        self.invest_percent = 0  # invest stock percent

        self.trend_data_dict = {}

    @property
    def get_start_money(self):
        return round(self.start_money, 0)

    @property
    def get_stock_type(self):
        return self.stock_type

    @property
    def get_realtime_money(self):
        return round(self.realtime_money, 0)

    @property
    def get_stock(self):
        return round(self.stock, 0)

    @property
    def get_number(self):
        return round(self.number, 0)

    @property
    def get_price(self):
        return round(self.price, 3)

    @property
    def get_average(self):
        return round(self.average, 3)

    @property
    def get_tax(self):
        return round(self.tax, 2)

    @property
    def get_profit(self):
        return round(self.profit, 0)

    @property
    def get_profit_percent(self):
        return round(self.profit_percent, 0)

    @property
    def get_invest_percent(self):
        return round(self.invest_percent, 0)

    @property
    def get_trend_data_dict(self):
        return self.trend_data_dict

    def reset_account(self, money=0.0, stock=0.0, stock_type='fund'):
        """
        Reset all data
        :param money: start money
        :param stock: start stock
        :param stock_type: type: 'fund', 'stock'
        :return: None
        """
        self.start_money = money  # start money
        self.stock_type = stock_type  # 'fund' or 'stock' for different tax

        self.realtime_money = money  # money in the account
        self.stock = stock  # stock money have bought in, number * average
        self.stock_invest = 0  # pay on the stock

        self.number = 0  # stock numbers *100
        self.price = 0  # stock price

        self.average = 0  # average stock price
        self.tax = 0  # tax when buy or sell

        self.profit = 0  # real time money - start_money = profit
        self.profit_percent = 0  # profit percent
        self.invest_percent = 0  # invest stock percent

        self.trend_data_dict = {}

    def buy(self, number_in, price_in):
        """
        Calculate the data base after buying stock
        :param number_in: the number of stock to buy in
        :param price_in: the price of stock to buy in
        :return: None
        """
        # check if money enough
        if self.realtime_money < (number_in * price_in):
            logger_account.error("money is not enough to buy.")
            return -1

        # calculate the tax
        self._buy_tax_calculation(number_in, price_in)

        # money
        self.realtime_money = self.realtime_money - self.tax
        self.realtime_money = self.realtime_money - (number_in * price_in)
        if self.realtime_money < 0:
            logger_account.error("money is not enough to buy.")
            return -1

        # money invest change
        self.stock_invest = self.stock_invest + (number_in * price_in)

        # stock change based on the price in and previous price
        tmp_diff = (price_in - self.price)
        if tmp_diff == 0 or self.number == 0:  # diff == 0 or the first time buy in
            self.stock = self.stock + (number_in * price_in)
        elif tmp_diff < 0:
            self.stock = (self.price - abs(tmp_diff)) * self.number + (number_in * price_in)  # lose and curent buy in
        else:
            self.stock = (self.price + abs(tmp_diff)) * self.number + (number_in * price_in)  # earn and curent buy in

        # number & price
        self.number = self.number + number_in
        self.price = price_in

        # average
        if self.stock_invest <= 0:
            self.average = 0
        else:
            self.average = self.stock_invest / self.number

        # profit change
        self.profit = round((self.realtime_money + self.stock) - self.start_money, 0)
        self.profit_percent = round((self.profit / self.start_money) * 100, 2)
        if self.average <= 0:
            self.invest_percent = 0
        else:
            self.invest_percent = round(((self.price - self.average) / self.average) * 100, 2)

    def sell(self, number_out, price_out):
        """
        Calculate the data base after selling stock
        :param number_in: the number of stock to buy in
        :param price_in: the price of stock to buy in
        :return: None
        """
        # check if stock enough to sell
        if self.number < number_out:
            logger_account.error("stock number is not enough to sell.")
            return -1

        # calculate the tax
        self._sell_tax_calculation(number_out, price_out)

        # money
        self.realtime_money = self.realtime_money - self.tax
        self.realtime_money = self.realtime_money + (number_out * price_out)

        # money invest change
        self.stock_invest = self.stock_invest - (number_out * price_out)

        # stock change based on the price in and previous price
        tmp_diff = (price_out - self.price)
        if tmp_diff == 0:
            self.stock = self.stock - (number_out * price_out)
        elif tmp_diff < 0:
            self.stock = (self.price - abs(tmp_diff)) * self.number - (number_out * price_out)  # loss and current sell
        else:
            self.stock = (self.price + abs(tmp_diff)) * self.number - (number_out * price_out)  # earn and current sell

        # number & price
        self.number = self.number - number_out
        self.price = price_out

        # average change
        if self.stock_invest <= 0:
            self.average = 0
        else:
            self.average = self.stock_invest / self.number

        # profit change
        self.profit = round((self.realtime_money + self.stock) - self.start_money, 0)
        self.profit_percent = round((self.profit / self.start_money) * 100, 2)
        if self.average <= 0:
            self.invest_percent = 0
        else:
            self.invest_percent = round(((self.price - self.average) / self.average) * 100, 2)

    def intelligent_buy(self, price_in):
        """
        Buy automactically
        If the price in lower than before, buy some of them, to let current stock to equal to previous stock
        :param price_in: price
        :return:intelligent cal numbers
        """
        # check if current stock less than before ,and enough to buy times of 100
        # intelligent to calculate the number, and use price in, to call buy function
        tmp_stock = price_in * self.number
        tmp_buy_num = (abs(self.stock - tmp_stock) / price_in)
        tmp_buy_num = round((tmp_buy_num / 100)) * 100

        if tmp_stock < self.stock:
            if tmp_buy_num >= 100:
                self.buy(tmp_buy_num, price_in)
                logger_account.info('Intelligently buy {} numbers.'.format(tmp_buy_num))
                return tmp_buy_num
            else:
                logger_account.warning('Not enough stock to sell intelligently.')
        else:
            logger_account.warning('Not enough stock to sell intelligently.')

    def intelligent_sell(self, price_out):
        """
        Sell automactically
        If the price in higher than before, sell some of them, to let current stock to equal to previous stock
        :param price_out: price
        :return:intelligent cal numbers
        """
        # check if current stock more than before ,and enough to sell times of 100
        # intelligent to calculate the number, and use price in, to call sell function
        tmp_stock = price_out * self.number
        tmp_sell_num = (abs(tmp_stock - self.stock) / price_out)
        tmp_sell_num = round((tmp_sell_num / 100)) * 100

        if tmp_stock > self.stock:
            if tmp_sell_num >= 100:
                self.sell(tmp_sell_num, price_out)
                logger_account.info('Intelligently sell {} numbers.'.format(tmp_sell_num))
                return tmp_sell_num
            else:
                logger_account.warning('Not enough stock to sell intelligently.')
        else:
            logger_account.warning('Not enough stock to sell intelligently.')

    def intelligent_auto_uptrend(self, base_number, multi_price):
        """
        Auto buy and sell in uptrend with multi price
        The strategy is that keep the stock in the base value
        :param base_number: number for first
        :param multi_price: multi price input
        :return: price list and profit list
        """
        # seperate the multi price from str to list, judge the price to decide using intellighent buy or sell
        # seperate price str list
        price_list = []
        price_str_list = multi_price.split()
        for i in range(len(price_str_list)):
            try:
                tmp_price_var = float(price_str_list[i])
                price_list.append(tmp_price_var)
            except:
                logger_account.error('price var {} change to float error.'.format(price_str_list[i]))

        # calculate intelligently
        profit_list = []
        average_list = []
        stock_list = []
        realtime_money_list = []
        for i in range(len(price_list)):
            if i == 0:
                self.buy(base_number, price_list[i])
                profit_list.append(self.get_profit)
                average_list.append(self.get_average)
                stock_list.append(self.get_stock)
                realtime_money_list.append(self.get_realtime_money)
            elif price_list[i] > price_list[i - 1]:
                self.intelligent_sell(price_list[i])
                profit_list.append(self.get_profit)
                average_list.append(self.get_average)
                stock_list.append(self.get_stock)
                realtime_money_list.append(self.get_realtime_money)
            elif price_list[i] == price_list[i - 1]:
                profit_list.append(self.get_profit)
                average_list.append(self.get_average)
                stock_list.append(self.get_stock)
                realtime_money_list.append(self.get_realtime_money)
            else:
                self.intelligent_buy(price_list[i])
                profit_list.append(self.get_profit)
                average_list.append(self.get_average)
                stock_list.append(self.get_stock)
                realtime_money_list.append(self.get_realtime_money)

        # change trend data dict content
        self.trend_data_dict['price_list'] = price_list
        self.trend_data_dict['profit_list'] = profit_list
        self.trend_data_dict['average_list'] = average_list
        self.trend_data_dict['stock_list'] = stock_list
        self.trend_data_dict['realtime_money_list'] = realtime_money_list

    def intelligent_auto_downtrend(self, base_number, multi_price):
        """
        Auto buy and sell in downtrend with multi price
        The strategy is that keep the stock in a value when rise, and buy times of base vale when downtrend
        :param base_number: number
        :param multi_price: multi price input
        :return: None
        """
        # seperate the multi price from str to list, judge the price to decide using intellighent buy or sell
        # seperate price str list
        price_list = []
        price_str_list = multi_price.split()
        for i in range(len(price_str_list)):
            try:
                tmp_price_var = float(price_str_list[i])
                price_list.append(tmp_price_var)
            except:
                logger_account.error('price var {} change to float error.'.format(price_str_list[i]))

        # calculate intelligently
        profit_list = []
        average_list = []
        stock_list = []
        realtime_money_list = []
        counter = 0
        for i in range(len(price_list)):
            if i == 0:
                counter = counter + 1
                self.buy(base_number * counter, price_list[i])
                profit_list.append(self.get_profit)
                average_list.append(self.get_average)
                stock_list.append(self.get_stock)
                realtime_money_list.append(self.get_realtime_money)
            elif price_list[i] > price_list[i - 1]:
                self.intelligent_sell(price_list[i])
                profit_list.append(self.get_profit)
                average_list.append(self.get_average)
                stock_list.append(self.get_stock)
                realtime_money_list.append(self.get_realtime_money)
            elif price_list[i] == price_list[i - 1]:
                profit_list.append(self.get_profit)
                average_list.append(self.get_average)
                stock_list.append(self.get_stock)
                realtime_money_list.append(self.get_realtime_money)
            else:
                counter = counter + 1
                self.buy(base_number * (counter), price_list[i])
                profit_list.append(self.get_profit)
                average_list.append(self.get_average)
                stock_list.append(self.get_stock)
                realtime_money_list.append(self.get_realtime_money)

        # change trend data dict content
        self.trend_data_dict['price_list'] = price_list
        self.trend_data_dict['profit_list'] = profit_list
        self.trend_data_dict['average_list'] = average_list
        self.trend_data_dict['stock_list'] = stock_list
        self.trend_data_dict['realtime_money_list'] = realtime_money_list

    def intelligent_auto_fixed(self, base_number, multi_price):
        """
        Auto buy in fixed price
        :param base_number: number
        :param multi_price: price list
        :return:
        """
        # seperate price str list
        price_list = []
        price_str_list = multi_price.split()
        for i in range(len(price_str_list)):
            try:
                tmp_price_var = float(price_str_list[i])
                price_list.append(tmp_price_var)
            except:
                logger_account.error('price var {} change to float error.'.format(price_str_list[i]))

        # calculate
        profit_list = []
        average_list = []
        stock_list = []
        realtime_money_list = []
        for i in range(len(price_list)):
            self.buy(base_number, price_list[i])
            profit_list.append(self.get_profit)
            average_list.append(self.get_average)
            stock_list.append(self.get_stock)
            realtime_money_list.append(self.get_realtime_money)

        # change trend data dict content
        self.trend_data_dict['price_list'] = price_list
        self.trend_data_dict['profit_list'] = profit_list
        self.trend_data_dict['average_list'] = average_list
        self.trend_data_dict['stock_list'] = stock_list
        self.trend_data_dict['realtime_money_list'] = realtime_money_list

    def _buy_tax_calculation(self, number_in, price_in):
        """
        Calculate the tax, fund for 1/1000, stock should more than 5 and 1.5/10000
        :param number_in: number
        :param price_in: price
        :return: tax
        """
        if self.stock_type == 'fund':
            self.tax = float((number_in * price_in) / 10000)
        elif self.stock_type == 'stock':
            tmp_tax = float((number_in * price_in) * 1.5 / 10000)
            if tmp_tax < 5:
                self.tax = 5
            else:
                self.tax = tmp_tax
        else:
            logger_account.error("stock type error.")

        return round(self.tax, 2)

    def _sell_tax_calculation(self, number_in, price_in):
        """
        Calculate the tax, fund for 1/1000, stock should more
        than 5 and 1.5/10000 & 1/1000 print tax
        :param number_in: number
        :param price_in: price
        :return: tax
        """
        if self.stock_type == 'fund':
            self.tax = float((number_in * price_in) / 10000)
        elif self.stock_type == 'stock':
            tmp_tax = float((number_in * price_in) * 1.5 / 10000)
            print_tax = float((number_in * price_in) / 1000)
            if tmp_tax < 5:
                self.tax = print_tax + 5
            else:
                self.tax = print_tax + tmp_tax
        else:
            logger_account.error("stock type error.")

        return round(self.tax, 2)


if __name__ == '__main__':
    # test app UI
    pass
